/**
  ******************************************************************************
  * @file    modbus_host.c
  * @author  ashuai0110
  * @version V2.1
  * @date    2023-10-25
  * @brief   modbus主机程序(RTU ASCII)
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "modbus_host.h"

/**
 * @addtogroup modbus
 * @{
 */

/**
 * @defgroup modbus_host modbus_host
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/
/**
 * @defgroup modbus_host_local_macros modbus host local macros
 * @{
 */

/**
 * @defgroup modbus_host_check_parameters_validity modbus host check parameters validity
 * @{
 */
/* modbus主机管理序号有效性检查 */
#define IS_VALID_MBH_CH(x)                                                    \
(       (x) < MBH_NUM)
/**
 * @}
 */

/**
 * @}
 */

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/
/**
 * @defgroup modbus_host_local_variables modbus host local variables
 * @{
 */

static mb_host_t mbHostArr[MBH_NUM]; /*!< modbus主机管理数组 */

/**
 * @}
 */

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/
/**
 * @defgroup modbus_host_local_functions modbus host local functions
 * @{
 */

/**
  * @brief  从机应答处理
  *
  * @param  ch : modbus主机管理序号(即数组索引)
  *
  * @retval None
  */
static void mb_host_rspProcess(uint8_t ch)
{
    switch(mbHostArr[ch].dataBuf[MB_CODE_OFF])
    {
        case 0x01: mb_host_rsp_01h(ch, mbHostArr[ch].regTmp, mbHostArr[ch].numTmp, &mbHostArr[ch].dataBuf[MB_RSP_DATA_OFF]); break; /* 读线圈应答处理 */
        case 0x02: mb_host_rsp_02h(ch, mbHostArr[ch].regTmp, mbHostArr[ch].numTmp, &mbHostArr[ch].dataBuf[MB_RSP_DATA_OFF]); break; /* 读离散量输入应答处理 */
        case 0x03: mb_host_rsp_03h(ch, mbHostArr[ch].regTmp, mbHostArr[ch].numTmp, &mbHostArr[ch].dataBuf[MB_RSP_DATA_OFF]); break; /* 读保持寄存器应答处理 */
        case 0x04: mb_host_rsp_04h(ch, mbHostArr[ch].regTmp, mbHostArr[ch].numTmp, &mbHostArr[ch].dataBuf[MB_RSP_DATA_OFF]); break; /* 读输入寄存器应答处理 */
        case 0x05: 
        case 0x06: 
        case 0x0F: 
        case 0x10: mb_host_rsp_05_10h(ch); break; /* 写命令应答处理 */
        /*
        用户在这里写其他功能码的应答处理
        */
        default: if(mbHostArr[ch].dataBuf[MB_CODE_OFF] & 0x80) { mb_host_rsp_err(ch, &mbHostArr[ch].dataBuf[MB_CODE_OFF]); } break; /* 不支持的功能码或错误返回 */
    }
}

/**
  * @brief  追加校验后发送
  *
  * @param  ch : modbus主机管理序号(即数组索引)
  *
  * @retval None
  */
static void mb_host_send_with_check(uint8_t ch)
{
    uint16_t checkVal;
    
    /* ASCII协议 */
    if(MB_ASCII == mbHostArr[ch].protocol)
    {
        /* 追加LRC校验结果 */
        checkVal = mb_check_lrc(mbHostArr[ch].dataBuf, mbHostArr[ch].dataLen);
        mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = checkVal;
        /* HEX转ASCII */
        mb_hex_to_ascii(&mbHostArr[ch].dataBuf[0], mbHostArr[ch].dataLen, &mbHostArr[ch].dataBuf[0]);
        mbHostArr[ch].dataLen = (mbHostArr[ch].dataLen << 1) + 3;
        mbHostArr[ch].dataBuf[0] = 0x3A;
        mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen - 2] = 0x0D;
        mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen - 1] = 0x0A;
    } /* RTU协议 */
    else
    {
        /* 追加CRC校验结果 */
        checkVal = mb_check_crc16(mbHostArr[ch].dataBuf, mbHostArr[ch].dataLen);
        mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = checkVal >> 8;
        mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = checkVal;
    }
#if _MBH_DEBUG
    PRINT_LOG("modbus host[%d] send: ", ch);
    for(uint16_t i = 0; i < mbHostArr[ch].dataLen; i++)
    {
        PRINT_LOG("%02X ", mbHostArr[ch].dataBuf[i]);
    }
    PRINT_LOG("\r\n");
#endif
    mbHostArr[ch].state = MB_TX_BUSY; /* 切换状态 */
    mbHostArr[ch].txEnFn(1); /* 开启发送 */
}

/**
  * @brief  功能码0x01 0x02 0x03 0x04命令
  *
  * @note   从机地址 | 功能码 | 起始地址 | 数量 | CRC16
  *            1     |   1    |    2     |  2   |   2   (占用字节)
  *
  * @param  ch        : modbus主机管理序号(即数组索引)
  *
  * @param  slaveAddr : 从机地址
  *
  * @param  funCode   : 功能码
  *
  * @param  reg       : 起始地址
  *
  * @param  num       : 数量
  *
  * @retval None
  */
static void mb_host_cmd_01_04h(uint8_t ch, uint8_t slaveAddr, uint8_t funCode, uint16_t reg, uint16_t num)
{
    mbHostArr[ch].dataLen = 0;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = slaveAddr;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = funCode;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = num >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = num;
    
    mb_host_send_with_check(ch);
}

/**
  * @brief  写单个线圈命令(0x05)
  *
  * @note   从机地址 | 功能码 | 起始地址 | 写入值 | CRC16
  *            1     |   1    |    2     |   2    |   2   (占用字节)
  *
  * @param  ch        : modbus主机管理序号(即数组索引)
  *
  * @param  slaveAddr : 从机地址
  *
  * @param  reg       : 起始地址
  *
  * @param  extend    : 扩展数据(一个线圈数据 0:OFF 1:ON)
  *
  * @retval None
  */
static void mb_host_cmd_05h(uint8_t ch, uint8_t slaveAddr, uint16_t reg, uint8_t extend)
{
    mbHostArr[ch].dataLen = 0;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = slaveAddr;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = 0x05;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = extend ? 0xFF : 0x00; /* 写入值:0xFF00=ON 0x0000=OFF */
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = 0x00;
    
    mb_host_send_with_check(ch);
}

/**
  * @brief  写单个保持寄存器命令(0x06)
  *
  * @note   从机地址 | 功能码 | 起始地址 | 写入值 | CRC16
  *            1     |   1    |    2     |   2    |   2   (占用字节)
  *
  * @param  ch        : modbus主机管理序号(即数组索引)
  *
  * @param  slaveAddr : 从机地址
  *
  * @param  reg       : 起始地址
  *
  * @param  extend    : 扩展数据(一个寄存器数据 16bit)
  *
  * @retval None
  */
static void mb_host_cmd_06h(uint8_t ch, uint8_t slaveAddr, uint16_t reg, uint16_t extend)
{
    mbHostArr[ch].dataLen = 0;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = slaveAddr;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = 0x06;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = extend >> 8; 
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = extend;
    
    mb_host_send_with_check(ch);
}

/**
  * @brief  写多个线圈命令(0x0f)
  *
  * @note   从机地址 | 功能码 | 起始地址 | 数量n | 字节数2n | 写入值 | CRC16
  *            1     |   1    |    2     |   2   |    1     |   2n   |   2   (占用字节)
  *
  * @param  ch        : modbus主机管理序号(即数组索引)
  *
  * @param  slaveAddr : 从机地址
  *
  * @param  reg       : 起始地址
  *
  * @param  num       : 数量
  *
  * @param  extend    : 扩展数据(多个线圈数据 1字节型指针)
  *
  * @retval None
  */
static void mb_host_cmd_0fh(uint8_t ch, uint8_t slaveAddr, uint16_t reg, uint16_t num, uint8_t *extend)
{
    uint16_t bytes = (num + 7) >> 3;
    
    mbHostArr[ch].dataLen = 0;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = slaveAddr;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = 0x0F;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = num >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = num;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = bytes;
    num = num % 8;
    if(num) { extend[bytes - 1] = extend[bytes - 1] & ((1u << num) - 1); } /* 未使用bit填0 */
    memcpy(&mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen], extend, bytes);
    mbHostArr[ch].dataLen += bytes;

    mb_host_send_with_check(ch);
}

/**
  * @brief  写多个寄存器命令(0x10)
  *
  * @note   从机地址 | 功能码 | 起始地址 | 数量n | 字节数2n | 写入值 | CRC16
  *            1     |    1   |    2     |   2   |    1     |   2n   |   2   (占用字节)
  *
  * @param  ch        : modbus主机管理序号(即数组索引)
  *
  * @param  slaveAddr : 从机地址
  *
  * @param  reg       : 起始地址
  *
  * @param  num       : 数量
  *
  * @param  extend    : 扩展数据(多个寄存器数据 2字节型指针)
  *
  * @retval None
  */
static void mb_host_cmd_10h(uint8_t ch, uint8_t slaveAddr, uint16_t reg, uint16_t num, uint16_t *extend)
{
    uint16_t bytes = num << 1;
    
    mbHostArr[ch].dataLen = 0;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = slaveAddr;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = 0x10;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = reg;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = num >> 8;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = num;
    mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = bytes;
    for(uint16_t cnt = 0; cnt < num; cnt++)
    {
        mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = extend[cnt] >> 8;
        mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen++] = extend[cnt];
    }

    mb_host_send_with_check(ch);
}

/**
 * @}
 */

/**
 * @defgroup modbus_host_global_functions modbus host global functions
 * @{
 */

/**
  * @brief  modbus主机初始化
  *
  * @param  ch         : modbus主机管理序号(即数组索引)
  *
  * @param  protocol   : 协议类型 @ref modbus_common_protocol
  *   @arg  MB_RTU     : RTU协议
  *   @arg  MB_ASCII   : ASCII协议
  *
  * @param  pBuf       : 接收数据缓存区
  *
  * @param  bufLen     : 接收数据缓存区长度
  *
  * @param  txEnFn     : 主机发送前(串口)准备函数
  *
  * @param  baud       : 波特率(内部转换为帧间隔超时时间,填0则帧间隔超时时间为0))
  *
  * @param  rspTimeout : 应答超时时间ms
  *
  * @retval None
  */
void mb_host_init(uint8_t ch, uint8_t protocol, void *pBuf, uint16_t bufLen, modbus_tx_en_t txEnFn, uint32_t baud, uint16_t rspTimeout)
{
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));
    ASSERT_PARAM(IS_VALID_MB_PROTOCOL(protocol));
    ASSERT_PARAM(IS_VALID_POINT(pBuf));
    ASSERT_PARAM(IS_VALID_POINT(txEnFn));
    
    mbHostArr[ch].dataBuf = (uint8_t *)pBuf;
    mbHostArr[ch].dataBufLen = bufLen;
    mbHostArr[ch].dataLen = 0;
    mbHostArr[ch].readLen = 0;
    
    mbHostArr[ch].protocol = protocol;
    mbHostArr[ch].state = MB_TX_IDLE;
    mbHostArr[ch].broadcast = 0;
    
    mb_host_set_frame_intv(ch, baud);
    mbHostArr[ch].curFrameIntv = 0;
    mbHostArr[ch].rspTimeout = rspTimeout;
    mbHostArr[ch].rspTimeoutCnt = 0;
    
    mbHostArr[ch].txEnFn = txEnFn;
}

/**
  * @brief  modbus主机轮询处理
  *
  * @note   轮询周期1ms
  *
  * @param  ch            : modbus主机管理序号(即数组索引)
  *
  * @retval uint8_t
  *    @arg RET_OK        : 处理完成
  *    @arg RET_ING       : 进行中
  *    @arg RET_TIMEOUT   : 超时
  *    @arg RET_LEN_ERR   : 数据长度错误
  *    @arg RET_CHECK_ERR : 数据校验错误
  *    @arg RET_FRAME_ERR : 数据格式错误
  */
uint8_t mb_host_poll(uint8_t ch)
{
    uint16_t checkVal, startIndex, stopIndex;
    uint8_t ret = RET_ING; /* 进行中 */
    
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));
    
    switch(mbHostArr[ch].state)
    {
        case MB_TX_IDLE: break;
        case MB_TX_BUSY: break;
        case MB_RX_IDLE:
            /* 应答超时判断 */
            if(mbHostArr[ch].rspTimeoutCnt && mbHostArr[ch].rspTimeoutCnt++ > mbHostArr[ch].rspTimeout)
            {
                mbHostArr[ch].rspTimeoutCnt = 0;
                ret = RET_TIMEOUT; /* 应答超时 */
                goto rspErr;
            }
            /* 帧间隔超时判断 */
            if(mbHostArr[ch].curFrameIntv && mbHostArr[ch].curFrameIntv++ > mbHostArr[ch].targetFrameIntv)
            {
                mbHostArr[ch].curFrameIntv = 0;
                mbHostArr[ch].state = MB_RX_BUSY; /* 切换状态 */
#if _MBH_DEBUG
                PRINT_LOG("modbus host[%d] recv raw data: ", ch);
                for(uint16_t i = 0; i < mbHostArr[ch].dataLen; i++)
                {
                    PRINT_LOG("%02X ", mbHostArr[ch].dataBuf[i]);
                }
                PRINT_LOG("\r\n");
#endif
            }
            break;
        case MB_RX_BUSY:
            /* ASCII协议数据检查和转换 */
            if(MB_ASCII == mbHostArr[ch].protocol)
            {
                /* 寻找起始符0x3A */
                for(startIndex = 0; startIndex < mbHostArr[ch].dataLen; startIndex++)
                {
                    if(0x3A == mbHostArr[ch].dataBuf[startIndex]) { break; }
                }
                /* 起始符索引检查 */
                if((mbHostArr[ch].dataLen - startIndex - 1) < 10)
                {
                    ret = RET_FRAME_ERR; /* 应答数据格式错误 */
                    goto rspErr;
                }
                /* 寻找结束符0x0D 0x0A */
                for(stopIndex = startIndex + 1; stopIndex < mbHostArr[ch].dataLen; stopIndex += 2)
                {
                    if(0x0D == mbHostArr[ch].dataBuf[stopIndex] && 0x0A == mbHostArr[ch].dataBuf[stopIndex + 1]) { break; }
                }
                /* 结束符索引检查 */
                if(stopIndex >= mbHostArr[ch].dataLen)
                {
                    ret = RET_FRAME_ERR; /* 应答数据格式错误 */
                    goto rspErr;
                }
                mbHostArr[ch].dataLen = stopIndex - startIndex - 1;
                /* ASCII转HEX */
                mb_ascii_to_hex(&mbHostArr[ch].dataBuf[startIndex + 1], mbHostArr[ch].dataLen, &mbHostArr[ch].dataBuf[0]);
                mbHostArr[ch].dataLen >>= 1;
#if _MBH_DEBUG
                PRINT_LOG("modbus host[%d] convert data: ", ch);
                for(uint16_t i = 0; i < mbHostArr[ch].dataLen; i++)
                {
                    PRINT_LOG("%02X ", mbHostArr[ch].dataBuf[i]);
                }
                PRINT_LOG("\r\n");
#endif
            }
            /* 应答的从机地址与请求时不同 */
            if(mbHostArr[ch].dataBuf[0] != mbHostArr[ch].slaveTmp)
            {
                ret = RET_ING; /* 进行中 */
                goto rspErr;
            }
            /* 应答数据长度判断 */
            if(MB_PDU_SIZE_MIN > mbHostArr[ch].dataLen)
            {
                ret = RET_LEN_ERR; /* 应答数据长度错误 */
                goto rspErr;
            }
            /* ASCII协议 */
            if(MB_ASCII == mbHostArr[ch].protocol)
            {
                /* LRC校验判断 */
                checkVal = mb_check_lrc(&mbHostArr[ch].dataBuf[0], mbHostArr[ch].dataLen - 1);
                if(checkVal != mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen - 1])
                {
                    ret = RET_CHECK_ERR; /* 应答数据校验错误 */
                    goto rspErr;
                }
            } /* RTU协议 */
            else
            {
                /* CRC校验判断 */
                checkVal = mb_check_crc16(&mbHostArr[ch].dataBuf[0], mbHostArr[ch].dataLen - 2);
                if(checkVal != ((mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen - 2] << 8) | mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen - 1]))
                {
                    ret = RET_CHECK_ERR; /* 应答数据校验错误 */
                    goto rspErr;
                }
            }
            /* 应答处理 */
            mb_host_rspProcess(ch);
            ret = RET_OK; /* 应答处理完成 */
rspErr:
            mbHostArr[ch].state = MB_TX_IDLE; /* 切换状态 */
            break;
        default: break;
    }
    
    return ret;
}

/**
  * @brief  主机发送请求
  *
  * @note   功能码 --- 扩展数据格式
  *         0x05   --- 一个线圈数据 1byte 0:OFF 1:ON 
  *         0x06   --- 一个寄存器数据 2byte
  *         0x0F   --- 多个线圈数据 1byte型指针
  *         0x10   --- 多个寄存器数据 2byte型指针
  *
  * @param  ch        : modbus主机管理序号(即数组索引)
  *
  * @param  slaveAddr : 从机地址
  *
  * @param  funCode   : 功能码
  *
  * @param  reg       : 起始地址
  *
  * @param  num       : 数量
  *
  * @param  extend    : 扩展数据(不同功能码对应格式不同)
  *
  * @retval uint8_t
  *    @arg RET_OK    : 成功
  *    @arg RET_ERR   : 失败(不支持的功能码或数量超出合理范围)
  */
uint8_t mb_host_send_cmd(uint8_t ch, uint8_t slaveAddr, uint8_t funCode, uint16_t reg, uint16_t num, void *extend)
{
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));
    
    if(MB_TX_IDLE != mbHostArr[ch].state) { return RET_ERR; }
    
    mbHostArr[ch].slaveTmp = slaveAddr;
    mbHostArr[ch].numTmp = num;
    mbHostArr[ch].regTmp = reg;
    if(slaveAddr) { mbHostArr[ch].broadcast = 0; }
    else { mbHostArr[ch].broadcast = 1; }
    
    switch(funCode)
    {
        case 0x01: /* 读线圈请求 */
        case 0x02: /* 读离散量输入请求 */
            if(MB_RD_COIL_NUM_MAX >= num && num)
            {
                mb_host_cmd_01_04h(ch, slaveAddr, funCode, reg, num);
            }
            break;
        case 0x03: /* 读保持寄存器请求 */
        case 0x04: /* 读输入寄存器请求 */
            if(MB_RD_REG_NUM_MAX >= num && num)
            {
                mb_host_cmd_01_04h(ch, slaveAddr, funCode, reg, num);
            }
            break;
        case 0x05: /* 写单个线圈请求 */
            mb_host_cmd_05h(ch, slaveAddr, reg, *(uint8_t *)extend);
            break;
        case 0x06: /* 写单个保持寄存器请求 */
            mb_host_cmd_06h(ch, slaveAddr, reg, *(uint16_t *)extend);
            break;
        case 0x0F: /* 写多个线圈请求 */
            if(MB_WR_COIL_NUM_MAX >= num && num)
            {
                mb_host_cmd_0fh(ch, slaveAddr, reg, num, (uint8_t *)extend);
            }
            break;
        case 0x10: /* 写多个寄存器请求 */
            if(MB_WR_REG_NUM_MAX >= num && num)
            {
                mb_host_cmd_10h(ch, slaveAddr, reg, num, (uint16_t *)extend);
            }
            break;
        /*
        用户在这里写其他功能码的发送处理
        */
        default: /* 不支持的请求 */
            break;
    }
    
    return (mbHostArr[ch].state == MB_TX_BUSY) ? RET_OK : RET_ERR;
}

/**
  * @brief  写入数据到接收缓冲区
  *
  * @note   一般加入到(串口)接收中断
  *
  * @param  ch      : modbus主机管理序号(即数组索引)
  *
  * @param  pBuf    : 数据缓存区
  *
  * @param  len     : 写入长度byte
  *
  * @retval uint16_t
  *    @arg !0      : 成功写入的数据数量
  *    @arg 0       : 失败(状态不对或写入已满)
  */
uint16_t mb_host_write_rx_buf(uint8_t ch, void *pBuf, uint16_t len)
{
    uint16_t unusedLen;
    
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));
    ASSERT_PARAM(IS_VALID_POINT(pBuf));
    
    if(MB_RX_IDLE == mbHostArr[ch].state)
    {
        unusedLen = mbHostArr[ch].dataBufLen - mbHostArr[ch].dataLen;
        len = (len > unusedLen) ? unusedLen : len;
        
        if(0 < len)
        {
            memcpy((void *)&mbHostArr[ch].dataBuf[mbHostArr[ch].dataLen], pBuf, len);
            mbHostArr[ch].dataLen += len;
            mbHostArr[ch].curFrameIntv = 1; /* 开启帧间隔超时计数 */
            mbHostArr[ch].rspTimeoutCnt = 0; /* 关闭应答超时计数 */
            
            return len;
        }
    }
    
    return 0;
}

/**
  * @brief  读出数据从发送缓冲区
  *
  * @note   一般加入到(串口)发送中断
  *
  * @param  ch      : modbus主机管理序号(即数组索引)
  *
  * @param  pBuf    : 数据存储区
  *
  * @param  len     : 读出长度byte
  *
  * @retval uint16_t
  *    @arg !0      : 成功读出的数据数量
  *    @arg 0       : 失败(状态不对或无数据可读)
  */
uint16_t mb_host_read_tx_buf(uint8_t ch, void *pBuf, uint16_t len)
{
    uint16_t unreadLen;
    
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));
    ASSERT_PARAM(IS_VALID_POINT(pBuf));
    
    if(MB_TX_BUSY == mbHostArr[ch].state)
    {
        unreadLen = mbHostArr[ch].dataLen - mbHostArr[ch].readLen;
        len = (len > unreadLen) ? unreadLen : len;
        
        if(0 < len)
        {
            memcpy(pBuf, (void *)&mbHostArr[ch].dataBuf[mbHostArr[ch].readLen], len);
            mbHostArr[ch].readLen += len;
            if(0 >= (mbHostArr[ch].dataLen - mbHostArr[ch].readLen))
            {
                mbHostArr[ch].dataLen = 0;
                mbHostArr[ch].readLen = 0;
                if(mbHostArr[ch].broadcast)
                {
                    mbHostArr[ch].state = MB_TX_IDLE; /* 切换状态 */
                }
                else
                {
                    mbHostArr[ch].rspTimeoutCnt = 1; /* 开启应答超时计数 */
                    mbHostArr[ch].state = MB_RX_IDLE; /* 切换状态 */
                }
            }
            
            return len;
        }
    }
    mbHostArr[ch].txEnFn(0); /* 关闭发送 */
    
    return 0;
}

/**
  * @brief  设置协议类型
  *
  * @note   初始化后仍可修改,需保持通信协议与设定协议一致,不然无法解析成功
  *
  * @param  ch       : modbus主机管理序号(即数组索引)
  *
  * @param  protocol : 协议类型 @ref modbus_common_protocol
  *   @arg  MB_RTU   : RTU协议
  *   @arg  MB_ASCII : ASCII协议
  *
  * @retval None
  */
void mb_host_set_protocol(uint8_t ch, uint8_t protocol)
{
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));
    ASSERT_PARAM(IS_VALID_MB_PROTOCOL(protocol));
    
    mbHostArr[ch].protocol = protocol;
}

/**
  * @brief  获取协议类型
  *
  * @param  ch       : modbus主机管理序号(即数组索引)
  *
  * @retval uint8_t  : 协议类型 @ref modbus_common_protocol
  *    @arg MB_RTU   : RTU协议
  *    @arg MB_ASCII : ASCII协议
  */
uint8_t mb_host_get_protocol(uint8_t ch)
{
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));

    return mbHostArr[ch].protocol;
}

/**
  * @brief  设置帧间隔超时时间
  *
  * @param  ch   : modbus主机管理序号(即数组索引)
  *
  * @param  baud : 波特率(内部转换为帧间隔超时时间,填0则帧间隔超时时间为0)
  *
  * @retval None
  */
void mb_host_set_frame_intv(uint8_t ch, uint32_t baud)
{
    if(9600 < baud)
    {
        mbHostArr[ch].targetFrameIntv = 3;
    }
    else if(0 == baud)
    {
        mbHostArr[ch].targetFrameIntv = 0;
    }
    else
    {
        mbHostArr[ch].targetFrameIntv = 10000 / baud * 3.5;
    }
}

/**
  * @brief  获取帧间隔超时时间
  *
  * @param  ch : modbus主机管理序号(即数组索引)
  *
  * @retval 帧间隔超时时间ms
  */
uint16_t mb_host_get_frame_intv(uint8_t ch)
{
    return mbHostArr[ch].targetFrameIntv;
}

/**
  * @brief  设置应答超时时间
  *
  * @param  ch      : modbus主机管理序号(即数组索引)
  *
  * @param  timeout : 应答超时时间ms
  *
  * @retval None
  */
void mb_host_set_rsp_timeout(uint8_t ch, uint16_t timeout)
{
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));
    
    mbHostArr[ch].rspTimeout = (timeout < 10) ? 10 : timeout;
}

/**
  * @brief  获取应答超时时间
  *
  * @param  ch : modbus主机管理序号(即数组索引)
  *
  * @retval 应答超时时间ms
  */
uint16_t mb_host_get_rsp_timeout(uint8_t ch)
{
    ASSERT_PARAM(IS_VALID_MBH_CH(ch));

    return mbHostArr[ch].rspTimeout;
}

/**
  * @brief  读线圈应答处理(0x01)
  *
  * @note   从机地址 | 功能码 | 字节数n | 线圈状态 | CRC16
  *             1    |   1    |    1    |    n     |   2   (占用字节)
  *
  * @param  ch    : modbus主机管理序号(即数组索引)
  *
  * @param  reg   : 起始地址
  *
  * @param  num   : 线圈数量 1线圈=1bit
  *
  * @param  pData : 线圈状态
  *
  * @retval None
  */
__WEAK void mb_host_rsp_01h(uint8_t ch, uint16_t reg, uint16_t num, uint8_t *pData)
{
#if _MBH_DEBUG
    uint8_t bytes = (num + 7) >> 3;
    
    PRINT_LOG("modbus host[%d] rsp01h> coilAddr:0x%X, coilNum:%d, data:", ch, reg, num);
    for(uint8_t i = 0; i < bytes; i++)
    {
        PRINT_LOG("%X ", pData[i]);
    }
    PRINT_LOG("\r\n");
#endif
}

/**
  * @brief  读离散量输入应答处理(0x02)
  *
  * @note   从机地址 | 功能码 | 字节数n | 离散量状态 | CRC16
  *             1    |   1    |    1    |     n      |   2   (占用字节)
  *
  * @param  ch    : modbus主机管理序号(即数组索引)
  *
  * @param  reg   : 起始地址
  *
  * @param  num   : 离散量数量 1离散量=1bit
  *
  * @param  pData : 离散量状态
  *
  * @retval None
  */
__WEAK void mb_host_rsp_02h(uint8_t ch, uint16_t reg, uint16_t num, uint8_t *pData)
{
#if _MBH_DEBUG
    uint8_t bytes = (num + 7) >> 3;
    
    PRINT_LOG("modbus host[%d] rsp02h> coilAddr:0x%X, coilNum:%d, data:", ch, reg, num);
    for(uint8_t i = 0; i < bytes; i++)
    {
        PRINT_LOG("%X ", pData[i]);
    }
    PRINT_LOG("\r\n");
#endif
}

/**
  * @brief  读保持寄存器应答处理(0x03)
  *
  * @note   从机地址 | 功能码 | 字节数n | 保持寄存器值 | CRC16
  *             1    |   1    |    1    |       n      |   2   (占用字节)
  *
  * @param  ch    : modbus主机管理序号(即数组索引)
  *
  * @param  reg   : 起始地址
  *
  * @param  num   : 寄存器数量 1寄存器=16bit
  *
  * @param  pData : 保持寄存器值
  *
  * @retval None
  */
__WEAK void mb_host_rsp_03h(uint8_t ch, uint16_t reg, uint16_t num, uint8_t *pData)
{
#if _MBH_DEBUG
    uint8_t bytes = num << 1;
    
    PRINT_LOG("modbus host[%d] rsp03h> regAddr:0x%X, regNum:%d, data:", ch, reg, num);
    for(uint8_t i = 0; i < bytes; i++)
    {
        PRINT_LOG("%X ", pData[i]);
    }
    PRINT_LOG("\r\n");
#endif
}

/**
  * @brief  读输入寄存器应答处理(0x04)
  *
  * @note   从机地址 | 功能码 | 字节数n | 输入寄存器值 | CRC16
  *             1    |   1    |    1    |       n      |   2   (占用字节)
  *
  * @param  ch    : modbus主机管理序号(即数组索引)
  *
  * @param  reg   : 起始地址
  *
  * @param  num   : 输入寄存器数量 1输入寄存器=16bit
  *
  * @param  pData : 输入寄存器值
  *
  * @retval None
  */
__WEAK void mb_host_rsp_04h(uint8_t ch, uint16_t reg, uint16_t num, uint8_t *pData)
{
#if _MBH_DEBUG
    PRINT_LOG("modbus host[%d] rsp04h> regAddr:0x%X, regNum:%d, data:", ch, reg, num);
    for(uint8_t i = 0; i < num; i++)
    {
        PRINT_LOG("%X ", (pData[i << 1] << 8) | pData[(i << 1) + 1]);
    }
    PRINT_LOG("\r\n");
#endif
}

/**
  * @brief  功能码0x05 0x06 0x0F 0x10应答处理
  *
  * @note   从机地址 | 功能码 | 起始地址 | 写入值/数量 | CRC16
  *             1    |   1    |    2     |      2      |   2   (占用字节)
  *
  * @param  None
  *
  * @retval None
  */
__WEAK void mb_host_rsp_05_10h(uint8_t ch)
{
#if _MBH_DEBUG
    PRINT_LOG("modbus host[%d] rsp05h_06h_0fh_10h> success\r\n", ch);
#endif
}

/**
  * @brief  错误码应答处理(功能码+0x80)
  *
  * @note   从机地址 | 错误功能码 | 错误代码 | CRC16
  *             1    |      1     |    1     |   2   (占用字节)
  *
  * @param  ch    : modbus主机管理序号(即数组索引)
  *
  * @param  pData : 错误数据(错误功能码和错误代码)
  *
  * @retval None
  */
__WEAK void mb_host_rsp_err(uint8_t ch, uint8_t *pData)
{
/*
    部分错误代码释义:
    0x01 - 非法功能     : 功能码无效
    0x02 - 非法数据地址 : 地址无效或地址和数量的组合无效
    0x03 - 非法数据值   : 数据值无效
    0x04 - 从机故障     : 当从机试图执行请求的操作时,发生了不可恢复的错误
*/
#if _MBH_DEBUG
    PRINT_LOG("modbus host[%d] rspErr> errFunCode:0x%X, errCode:0x%X\r\n", ch, pData[0], pData[1]);
#endif
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */
